Motivation for this separate notebook: PCA does an ok job parsing out the different DDM variable types but EFA doesn’t. Initially I thought theoretically EFA would be more approrpriate since I am trying to capture ‘latent variables’ and didn’t want to assume that the variables don’t have any unique variance. I was particularly reluctant to do this since the same task correlations for all variable types were higher than correlations across tasks.

My initial attempt to run EFA on EZ variables from test data gave some errors, the fit wasn’t particularly good and the variable types did not really cluster separately. There was a cluster that included all the drift rates and another that included the majority of non-decision times. One could make an argument on using these clusters only as they capture separable processes but seems weak considering the bad fit of the model.

My earlier efforts (pre Neuroecon) used PCA. These did a better job in clustering the different variable types. This isn’t too surprising given that PCA will assume all variability within a measure is due to common variance. One could build an argument suggesting that PCA could/should be used in this case because even if the clusters are not necessarily reflecting common cognitive processes they method serves the purpose of reducing the dimensionality of rich data. This too seems weak if it’s not going to add anything to our understanding of the cognitive processes and will remain a primarily stastistical exercise.

More rencetly I am confused by other papers’ use of PCA. E.g. the Econographics paper suggests: > To summarize these clusters, we make use of principal components analysis (PCA), a statistical technique that produces components—linear combinations of variables—that explain as much variation in the underlying behaviors as possible, as discussed in Section 3. These components highlight latent dimensions underlying the econographic variables.

All of this is obviously very post-hoc. How should I go about this decision?

library(tidyverse)
theme_set(theme_bw())
options(scipen = 1, digits = 4)

helper_func_path = '/Users/zeynepenkavi/Dropbox/PoldrackLab/SRO_Retest_Analyses/code/helper_functions/'
source(paste0(helper_func_path, 'get_numeric_cols.R'))
source(paste0(helper_func_path, 'remove_outliers.R'))
source(paste0(helper_func_path, 'remove_correlated_task_variables.R'))
source(paste0(helper_func_path, 'transform_remove_skew.R'))
source(paste0(helper_func_path, 'get_demographics.R'))
source(paste0(helper_func_path, 'residualize_baseline.R'))
source(paste0(helper_func_path, 'find_optimal_components.R'))

ddm_workspace_scripts = '/Users/zeynepenkavi/Dropbox/PoldrackLab/SRO_DDM_Analyses/code/workspace_scripts/'
source(paste0(ddm_workspace_scripts,'ddm_measure_labels.R'))
source(paste0(ddm_workspace_scripts,'ddm_subject_data.R'))
rm(test_data_hddm_fullfit, test_data_hddm_refit)
clean_test_data = remove_correlated_task_variables(test_data)
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Dropping 137 variables with correlations above 0.85
motor_selective_stop_signal.hddm_drift
adaptive_n_back.std_rt
adaptive_n_back.avg_rt_error
adaptive_n_back.std_rt_error
attention_network_task.EZ_drift_incongruent
attention_network_task.hddm_drift
attention_network_task.conflict_acc
attention_network_task.EZ_non_decision_congruent
attention_network_task.EZ_non_decision_neutral
attention_network_task.EZ_thresh_incongruent
attention_network_task.conflict_acc
attention_network_task.congruent_rt
attention_network_task.incongruent_rt
attention_network_task.neutral_rt
attention_network_task.hddm_drift_incongruent
attention_network_task.neutral_rt
choice_reaction_time.hddm_drift
directed_forgetting.EZ_drift_pos
directed_forgetting.acc
directed_forgetting.hddm_drift
directed_forgetting.acc
directed_forgetting.hddm_drift
directed_forgetting.EZ_non_decision_pos
directed_forgetting.avg_rt
dot_pattern_expectancy.avg_rt
dot_pattern_expectancy.hddm_drift_AY
dot_pattern_expectancy.hddm_drift
dot_pattern_expectancy.EZ_non_decision_AX
local_global_letter.EZ_drift_global
local_global_letter.EZ_drift_switch
local_global_letter.acc
local_global_letter.hddm_drift
local_global_letter.hddm_drift
local_global_letter.hddm_drift
local_global_letter.EZ_non_decision_congruent
local_global_letter.EZ_non_decision_global
local_global_letter.EZ_non_decision_local
local_global_letter.EZ_non_decision_neutral
local_global_letter.EZ_non_decision_stay
local_global_letter.EZ_non_decision_switch
local_global_letter.avg_rt
local_global_letter.congruent_rt
local_global_letter.EZ_non_decision_local
local_global_letter.EZ_non_decision_stay
local_global_letter.EZ_non_decision_switch
local_global_letter.congruent_rt
local_global_letter.EZ_non_decision_neutral
local_global_letter.EZ_non_decision_switch
local_global_letter.avg_rt
local_global_letter.incongruent_rt
local_global_letter.local_incongruent_rt
local_global_letter.EZ_non_decision_stay
local_global_letter.EZ_non_decision_switch
local_global_letter.EZ_non_decision_switch
local_global_letter.avg_rt
local_global_letter.congruent_rt
local_global_letter.EZ_thresh_global
local_global_letter.EZ_thresh_incongruent
local_global_letter.EZ_thresh_stay
local_global_letter.EZ_thresh_switch
local_global_letter.congruent_rt
local_global_letter.global_congruent_rt
local_global_letter.incongruent_rt
local_global_letter.local_congruent_rt
local_global_letter.conflict_acc
local_global_letter.incongruent_harm_acc
local_global_letter.hddm_drift_incongruent
local_global_letter.global_congruent_rt
local_global_letter.local_congruent_rt
local_global_letter.global_bias_acc
local_global_letter.incongruent_rt
local_global_letter.local_incongruent_rt
local_global_letter.switch_cost_acc
recent_probes.EZ_drift_xrec
recent_probes.hddm_drift
recent_probes.EZ_drift_xrec_pos
recent_probes.hddm_drift
recent_probes.EZ_non_decision_xrec
recent_probes.EZ_non_decision_xrec_neg
shape_matching.hddm_drift
simon.EZ_drift_incongruent
simon.hddm_drift
simon.hddm_drift
simon.EZ_non_decision_congruent
simon.EZ_non_decision_incongruent
simon.EZ_thresh_incongruent
simon.congruent_sd_rt
simon.incongruent_acc
simon.congruent_avg_rt
simon.incongruent_avg_rt
simon.incongruent_avg_rt
simon.std_rt
simon.simon_acc
simon.std_rt
stim_selective_stop_signal.ignore_rt_error
stim_selective_stop_signal.stop_rt_error
stim_selective_stop_signal.stop_rt_error
motor_selective_stop_signal.hddm_drift
stim_selective_stop_signal.ignore_rt_error
stim_selective_stop_signal.stop_rt_error
stim_selective_stop_signal.stop_rt_error
stop_signal.hddm_drift
stop_signal.SSRT_high
stop_signal.SSRT_low
stop_signal.stop_rt_error
stop_signal.total_errors
stroop.EZ_drift_incongruent
stroop.hddm_drift
stroop.hddm_drift
stroop.EZ_non_decision_congruent
stroop.EZ_non_decision_incongruent
stroop.avg_rt
stroop.congruent_rt
stroop.congruent_rt
stroop.avg_rt
stroop.incongruent_rt
stroop.EZ_thresh_incongruent
stroop.congruent_rt
stroop.incongruent_rt
stroop.incongruent_rt
stroop.incongruent_errors
threebytwo.EZ_drift_task_switch_100.0
threebytwo.EZ_drift_task_switch_900.0
threebytwo.acc
threebytwo.hddm_drift
threebytwo.acc
threebytwo.hddm_drift
threebytwo.hddm_drift
threebytwo.EZ_non_decision_cue_switch_100.0
threebytwo.EZ_non_decision_task_switch_100.0
threebytwo.EZ_non_decision_task_switch_900.0
threebytwo.EZ_non_decision_task_switch_100.0
threebytwo.avg_rt
threebytwo.EZ_thresh_task_switch_100.0
threebytwo.EZ_thresh_task_switch_900.0
threebytwo.hddm_drift
threebytwo.avg_rt_error
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

Remove outliers (>2.5 SD away)

clean_test_data = cbind(sub_id = clean_test_data$sub_id, as.data.frame(apply(clean_test_data[, -which(names(clean_test_data) %in% c("sub_id"))], 2, remove_outliers)))

Transform skewed variables

numeric_cols = get_numeric_cols()
numeric_cols = numeric_cols[numeric_cols %in% names(clean_test_data) == T]
clean_test_data = transform_remove_skew(clean_test_data, numeric_cols)
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18 data positively skewed data were transformed:
adaptive_n_back.missed_percent
attention_network_task.avg_rt_error
attention_network_task.missed_percent
attention_network_task.std_rt_error
directed_forgetting.missed_percent
dot_pattern_expectancy.AX_errors
dot_pattern_expectancy.AY_errors
dot_pattern_expectancy.BX_errors
dot_pattern_expectancy.hddm_thresh
dot_pattern_expectancy.missed_percent
local_global_letter.global_congruent_errors
local_global_letter.local_congruent_errors
local_global_letter.missed_percent
shape_matching.missed_percent
simon.std_rt_error
stim_selective_stop_signal.hddm_thresh
stroop.missed_percent
threebytwo.missed_percent
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
13 positively skewed data could not be transformed successfully:
adaptive_n_back.missed_percent
attention_network_task.avg_rt_error
attention_network_task.missed_percent
attention_network_task.std_rt_error
directed_forgetting.missed_percent
dot_pattern_expectancy.missed_percent
local_global_letter.global_congruent_errors
local_global_letter.local_congruent_errors
local_global_letter.missed_percent
shape_matching.missed_percent
simon.std_rt_error
stroop.missed_percent
threebytwo.missed_percent
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8 data negatively skewed data were transformed:
attention_network_task.acc
choice_reaction_time.acc
dot_pattern_expectancy.acc
motor_selective_stop_signal.ignore_acc
shape_matching.acc
simon.congruent_acc
stim_selective_stop_signal.go_acc
stim_selective_stop_signal.ignore_acc
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6 negatively skewed data could not be transformed successfully:
choice_reaction_time.acc
dot_pattern_expectancy.acc
motor_selective_stop_signal.ignore_acc
shape_matching.acc
simon.congruent_acc
stim_selective_stop_signal.go_acc
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

Drop subject identifier column, mean impute and drop cols with no variance

clean_test_data_std = clean_test_data %>% mutate_if(is.numeric, scale)
clean_test_data_std = clean_test_data_std %>% select(-sub_id)

#mean imputation
clean_test_data_std[is.na(clean_test_data_std)]=0

#drop cols with no variance
clean_test_data_std = clean_test_data_std %>%
  select_if(function(col) sd(col) != 0)

Extract EZ and HDDM variables

clean_test_data_ez = clean_test_data_std %>%
  select(grep('EZ', names(clean_test_data_std), value=T))

clean_test_data_hddm = clean_test_data_std %>%
  select(grep('hddm', names(clean_test_data_std), value=T))

Residualize Age and Sex effects

data_path = '/Users/zeynepenkavi/Documents/PoldrackLabLocal/Self_Regulation_Ontology/Data/'
release = 'Complete_03-29-2018/'
dataset = 'demographic_health.csv'

demographics = get_demographics(dataset = paste0(data_path, release, dataset))

demographics = demographics %>%
  filter(X %in% clean_test_data$sub_id)
clean_test_data_ez = cbind(clean_test_data_ez, demographics[,c("Age", "Sex")])

res_clean_test_data_ez = residualize_baseline(clean_test_data_ez)
Warning: Unknown variables: `X`, `sub_id`, `subj_id`
res_clean_test_data_ez

PCA on EZ variables of test data

pca_ez_t1_comp_metrics = find_optimal_components(res_clean_test_data_ez, minc=2, maxc=20, model = "PCA")
pca_ez_t1_comp_metrics
ez_t1_pca_3 = principal(res_clean_test_data_ez, nfactors=3, rotate="oblimin")
Warning in cor.smooth(r): Matrix was not positive definite, smoothing was
done
The determinant of the smoothed correlation was zero.
This means the objective function is not defined for the null model either.
The Chi square is thus based upon observed correlations.
Warning in principal(res_clean_test_data_ez, nfactors = 3, rotate =
"oblimin"): The matrix is not positive semi-definite, scores found from
Structure loadings
data.frame(ez_t1_pca_3$values) %>%
  rename(eig = ez_t1_pca_3.values) %>%
  arrange(-eig) %>%
  mutate(var_pct = eig/sum(eig)*100,
         pc = 1:n()) %>%
  filter(pc<11)%>%
  ggplot(aes(factor(pc), var_pct))+
  geom_bar(stat="identity")+
  ylab("Percentage of variance explained")+
  xlab("Principal component")

data.frame(ez_t1_pca_3$values) %>%
  rename(eig = ez_t1_pca_3.values) %>%
  arrange(-eig) %>%
  mutate(var_pct = eig/sum(eig)*100,
         pc = 1:n(),
         var_pct_shift = lead(var_pct),
         var_pct_diff = var_pct - var_pct_shift) %>%
  filter(pc<11) %>%
  arrange(-var_pct_diff)
ez_t1_pca_3_loadings = as.data.frame(ez_t1_pca_3$loadings[])

ez_t1_pca_3_loadings[abs(ez_t1_pca_3_loadings)<0.3]=NA

tmp = ez_t1_pca_3_loadings %>%
  mutate(dv = row.names(.)) %>%
  select(dv, TC1, TC2, TC3) %>%
  mutate(num_loading = 3-(is.na(TC1)+is.na(TC2)+is.na(TC3) ) ) %>%
  filter(num_loading!=0) %>%
  select(-num_loading) %>%
  arrange(-TC1, -TC2, -TC3) %>%
  mutate(order_num = 1:n(),
         dv = reorder(dv, -order_num)) %>%
  select(-order_num) %>%
  gather(Factor, Loading, -dv) %>%
  na.exclude() %>%
  mutate(load_sign = factor(ifelse(Loading>0,"pos","neg")))

var_type = ifelse(grepl("drift", tmp$dv), "#7fc97f",
                  ifelse(grepl("thresh", tmp$dv), "#beaed4",
                         ifelse(grepl("non_dec", tmp$dv), "#fdc086", NA)))         
tmp%>%         
  ggplot(aes(dv, abs(Loading), fill=load_sign))+
  geom_bar(stat = "identity")+
  facet_wrap(~Factor, nrow=1)+
  coord_flip()+
  xlab("")+
  theme(legend.position = "none",
        axis.text.y = element_text(color = var_type))+
  ylab("Absolute Loading")

EFA on EZ variables of test data

efa_ez_t1_comp_metrics = find_optimal_components(res_clean_test_data_ez, fm = "minres", minc=2)
efa_ez_t1_comp_metrics
ez_t1_fa_8 = fa(res_clean_test_data_ez, efa_ez_t1_comp_metrics$comp[1], rotate='oblimin', fm='minres', scores='tenBerge')
Warning in cor.smooth(R): Matrix was not positive definite, smoothing was
done

Warning in cor.smooth(R): Matrix was not positive definite, smoothing was
done

Warning in cor.smooth(R): Matrix was not positive definite, smoothing was
done
Warning in cor.smooth(r): Matrix was not positive definite, smoothing was
done
The determinant of the smoothed correlation was zero.
This means the objective function is not defined for the null model either.
The Chi square is thus based upon observed correlations.
The estimated weights for the factor scores are probably incorrect.  Try a different factor extraction method.
Warning in cor.smooth(r): Matrix was not positive definite, smoothing was
done
ez_t1_fa_3 = fa(res_clean_test_data_ez, 3, rotate='oblimin', fm='minres', scores='tenBerge')
Warning in cor.smooth(R): Matrix was not positive definite, smoothing was
done

Warning in cor.smooth(R): Matrix was not positive definite, smoothing was
done

Warning in cor.smooth(R): Matrix was not positive definite, smoothing was
done
Warning in cor.smooth(r): Matrix was not positive definite, smoothing was
done
The determinant of the smoothed correlation was zero.
This means the objective function is not defined for the null model either.
The Chi square is thus based upon observed correlations.
The estimated weights for the factor scores are probably incorrect.  Try a different factor extraction method.
Warning in cor.smooth(r): Matrix was not positive definite, smoothing was
done
anova(ez_t1_fa_3, ez_t1_fa_8)
ez_t1_fa_3_loadings = as.data.frame(ez_t1_fa_3$loadings[])

ez_t1_fa_3_loadings[abs(ez_t1_fa_3_loadings)<0.3]=NA

tmp = ez_t1_fa_3_loadings %>%
  mutate(dv = row.names(.)) %>%
  select(dv, MR1, MR2, MR3) %>%
  mutate(num_loading = 3-(is.na(MR1)+is.na(MR2)+is.na(MR3) ) ) %>%
  filter(num_loading!=0) %>%
  select(-num_loading) %>%
  arrange(-MR1, -MR2, -MR3) %>%
  mutate(order_num = 1:n(),
         dv = reorder(dv, -order_num)) %>%
  select(-order_num) %>%
  gather(Factor, Loading, -dv) %>%
  na.exclude() %>%
  mutate(load_sign = factor(ifelse(Loading>0,"pos","neg")))

var_type = ifelse(grepl("drift", tmp$dv), "#7fc97f",
                  ifelse(grepl("thresh", tmp$dv), "#beaed4",
                         ifelse(grepl("non_dec", tmp$dv), "#fdc086", NA)))         
tmp%>%         
  ggplot(aes(dv, abs(Loading), fill=load_sign))+
  geom_bar(stat = "identity")+
  facet_wrap(~Factor, nrow=1)+
  coord_flip()+
  xlab("")+
  theme(legend.position = "none",
        axis.text.y = element_text(color = var_type))+
  ylab("Absolute Loading")

ez_t1_fa_8_loadings = as.data.frame(ez_t1_fa_8$loadings[])

ez_t1_fa_8_loadings[abs(ez_t1_fa_8_loadings)<0.3]=NA

tmp = ez_t1_fa_8_loadings %>%
  mutate(dv = row.names(.)) %>%
  select(dv, MR1, MR2, MR3, MR4, MR5, MR6, MR7, MR8) %>%
  mutate(num_loading = 8-(is.na(MR1)+is.na(MR2)+is.na(MR3)+is.na(MR4)+is.na(MR5)+is.na(MR6)+is.na(MR7)+is.na(MR8) ) ) %>%
  filter(num_loading!=0) %>%
  select(-num_loading) %>%
  arrange(-MR1, -MR2, -MR3,-MR1, -MR5, -MR6,-MR7, -MR8) %>%
  mutate(order_num = 1:n(),
         dv = reorder(dv, -order_num)) %>%
  select(-order_num) %>%
  gather(Factor, Loading, -dv) %>%
  na.exclude() %>%
  mutate(load_sign = factor(ifelse(Loading>0,"pos","neg")))

var_type = ifelse(grepl("drift", tmp$dv), "#7fc97f",
                  ifelse(grepl("thresh", tmp$dv), "#beaed4",
                         ifelse(grepl("non_dec", tmp$dv), "#fdc086", NA)))         
tmp%>%         
  ggplot(aes(dv, abs(Loading), fill=load_sign))+
  geom_bar(stat = "identity")+
  facet_wrap(~Factor, nrow=1)+
  coord_flip()+
  xlab("")+
  theme(legend.position = "none",
        axis.text.y = element_text(color = var_type))+
  ylab("Absolute Loading")

PCA on HDDM variables of test data

EFA on HDDM variables of test data

PCA on EZ variables of retest data

EFA on EZ variables of retest data

PCA on HDDM variables of retest data

EFA on HDDM variables of retest data

LS0tCnRpdGxlOiAnRGlmZmVyZW50IGFwcHJvYWNoZXMgdG8gZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIG9mIERETSB2YXJpYWJsZXMnCm91dHB1dDoKZ2l0aHViX2RvY3VtZW50Ogp0b2M6IHllcwp0b2NfZmxvYXQ6IHllcwotLS0KCk1vdGl2YXRpb24gZm9yIHRoaXMgc2VwYXJhdGUgbm90ZWJvb2s6IFBDQSBkb2VzIGFuIG9rIGpvYiBwYXJzaW5nIG91dCB0aGUgZGlmZmVyZW50IERETSB2YXJpYWJsZSB0eXBlcyBidXQgRUZBIGRvZXNuJ3QuIEluaXRpYWxseSBJIHRob3VnaHQgdGhlb3JldGljYWxseSBFRkEgd291bGQgYmUgbW9yZSBhcHByb3JwcmlhdGUgc2luY2UgSSBhbSB0cnlpbmcgdG8gY2FwdHVyZSAnbGF0ZW50IHZhcmlhYmxlcycgYW5kIGRpZG4ndCB3YW50IHRvIGFzc3VtZSB0aGF0IHRoZSB2YXJpYWJsZXMgZG9uJ3QgaGF2ZSBhbnkgdW5pcXVlIHZhcmlhbmNlLiBJIHdhcyBwYXJ0aWN1bGFybHkgcmVsdWN0YW50IHRvIGRvIHRoaXMgc2luY2UgdGhlIHNhbWUgdGFzayBjb3JyZWxhdGlvbnMgZm9yIGFsbCB2YXJpYWJsZSB0eXBlcyB3ZXJlIGhpZ2hlciB0aGFuIGNvcnJlbGF0aW9ucyBhY3Jvc3MgdGFza3MuCgpNeSBpbml0aWFsIGF0dGVtcHQgdG8gcnVuIEVGQSBvbiBFWiB2YXJpYWJsZXMgZnJvbSB0ZXN0IGRhdGEgZ2F2ZSBzb21lIGVycm9ycywgdGhlIGZpdCB3YXNuJ3QgcGFydGljdWxhcmx5IGdvb2QgYW5kIHRoZSB2YXJpYWJsZSB0eXBlcyBkaWQgbm90IHJlYWxseSBjbHVzdGVyIHNlcGFyYXRlbHkuIFRoZXJlIHdhcyBhIGNsdXN0ZXIgdGhhdCBpbmNsdWRlZCBhbGwgdGhlIGRyaWZ0IHJhdGVzIGFuZCBhbm90aGVyIHRoYXQgaW5jbHVkZWQgdGhlIG1ham9yaXR5IG9mIG5vbi1kZWNpc2lvbiB0aW1lcy4gT25lIGNvdWxkIG1ha2UgYW4gYXJndW1lbnQgb24gdXNpbmcgdGhlc2UgY2x1c3RlcnMgb25seSBhcyB0aGV5IGNhcHR1cmUgc2VwYXJhYmxlIHByb2Nlc3NlcyBidXQgc2VlbXMgd2VhayBjb25zaWRlcmluZyB0aGUgYmFkIGZpdCBvZiB0aGUgbW9kZWwuCgpNeSBlYXJsaWVyIGVmZm9ydHMgKHByZSBOZXVyb2Vjb24pIHVzZWQgUENBLiBUaGVzZSBkaWQgYSBiZXR0ZXIgam9iIGluIGNsdXN0ZXJpbmcgdGhlIGRpZmZlcmVudCB2YXJpYWJsZSB0eXBlcy4gVGhpcyBpc24ndCB0b28gc3VycHJpc2luZyBnaXZlbiB0aGF0IFBDQSB3aWxsIGFzc3VtZSBhbGwgdmFyaWFiaWxpdHkgd2l0aGluIGEgbWVhc3VyZSBpcyBkdWUgdG8gY29tbW9uIHZhcmlhbmNlLiBPbmUgY291bGQgYnVpbGQgYW4gYXJndW1lbnQgc3VnZ2VzdGluZyB0aGF0IFBDQSBjb3VsZC9zaG91bGQgYmUgdXNlZCBpbiB0aGlzIGNhc2UgYmVjYXVzZSBldmVuIGlmIHRoZSBjbHVzdGVycyBhcmUgbm90IG5lY2Vzc2FyaWx5IHJlZmxlY3RpbmcgY29tbW9uIGNvZ25pdGl2ZSBwcm9jZXNzZXMgdGhleSBtZXRob2Qgc2VydmVzIHRoZSBwdXJwb3NlIG9mIHJlZHVjaW5nIHRoZSBkaW1lbnNpb25hbGl0eSBvZiByaWNoIGRhdGEuIFRoaXMgdG9vIHNlZW1zIHdlYWsgaWYgaXQncyBub3QgZ29pbmcgdG8gYWRkIGFueXRoaW5nIHRvIG91ciB1bmRlcnN0YW5kaW5nIG9mIHRoZSBjb2duaXRpdmUgcHJvY2Vzc2VzIGFuZCB3aWxsIHJlbWFpbiBhIHByaW1hcmlseSBzdGFzdGlzdGljYWwgZXhlcmNpc2UuCgpNb3JlIHJlbmNldGx5IEkgYW0gY29uZnVzZWQgYnkgb3RoZXIgcGFwZXJzJyB1c2Ugb2YgUENBLiBFLmcuIHRoZSBFY29ub2dyYXBoaWNzIHBhcGVyIHN1Z2dlc3RzOgo+IFRvIHN1bW1hcml6ZSB0aGVzZSBjbHVzdGVycywgd2UgbWFrZSB1c2Ugb2YgcHJpbmNpcGFsIGNvbXBvbmVudHMgYW5hbHlzaXMgKFBDQSksIGEgc3RhdGlzdGljYWwgdGVjaG5pcXVlIHRoYXQgcHJvZHVjZXMgY29tcG9uZW50c+KAlGxpbmVhciBjb21iaW5hdGlvbnMgb2YgdmFyaWFibGVz4oCUdGhhdCBleHBsYWluIGFzIG11Y2ggdmFyaWF0aW9uIGluIHRoZSB1bmRlcmx5aW5nIGJlaGF2aW9ycyBhcyBwb3NzaWJsZSwgYXMgZGlzY3Vzc2VkIGluIFNlY3Rpb24gMy4gVGhlc2UgY29tcG9uZW50cyBoaWdobGlnaHQgbGF0ZW50IGRpbWVuc2lvbnMgdW5kZXJseWluZyB0aGUgZWNvbm9ncmFwaGljIHZhcmlhYmxlcy4KCkFsbCBvZiB0aGlzIGlzIG9idmlvdXNseSB2ZXJ5IHBvc3QtaG9jLiBIb3cgc2hvdWxkIEkgZ28gYWJvdXQgdGhpcyBkZWNpc2lvbj8KCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKdGhlbWVfc2V0KHRoZW1lX2J3KCkpCm9wdGlvbnMoc2NpcGVuID0gMSwgZGlnaXRzID0gNCkKCmhlbHBlcl9mdW5jX3BhdGggPSAnL1VzZXJzL3pleW5lcGVua2F2aS9Ecm9wYm94L1BvbGRyYWNrTGFiL1NST19SZXRlc3RfQW5hbHlzZXMvY29kZS9oZWxwZXJfZnVuY3Rpb25zLycKc291cmNlKHBhc3RlMChoZWxwZXJfZnVuY19wYXRoLCAnZ2V0X251bWVyaWNfY29scy5SJykpCnNvdXJjZShwYXN0ZTAoaGVscGVyX2Z1bmNfcGF0aCwgJ3JlbW92ZV9vdXRsaWVycy5SJykpCnNvdXJjZShwYXN0ZTAoaGVscGVyX2Z1bmNfcGF0aCwgJ3JlbW92ZV9jb3JyZWxhdGVkX3Rhc2tfdmFyaWFibGVzLlInKSkKc291cmNlKHBhc3RlMChoZWxwZXJfZnVuY19wYXRoLCAndHJhbnNmb3JtX3JlbW92ZV9za2V3LlInKSkKc291cmNlKHBhc3RlMChoZWxwZXJfZnVuY19wYXRoLCAnZ2V0X2RlbW9ncmFwaGljcy5SJykpCnNvdXJjZShwYXN0ZTAoaGVscGVyX2Z1bmNfcGF0aCwgJ3Jlc2lkdWFsaXplX2Jhc2VsaW5lLlInKSkKc291cmNlKHBhc3RlMChoZWxwZXJfZnVuY19wYXRoLCAnZmluZF9vcHRpbWFsX2NvbXBvbmVudHMuUicpKQoKZGRtX3dvcmtzcGFjZV9zY3JpcHRzID0gJy9Vc2Vycy96ZXluZXBlbmthdmkvRHJvcGJveC9Qb2xkcmFja0xhYi9TUk9fRERNX0FuYWx5c2VzL2NvZGUvd29ya3NwYWNlX3NjcmlwdHMvJwpzb3VyY2UocGFzdGUwKGRkbV93b3Jrc3BhY2Vfc2NyaXB0cywnZGRtX21lYXN1cmVfbGFiZWxzLlInKSkKc291cmNlKHBhc3RlMChkZG1fd29ya3NwYWNlX3NjcmlwdHMsJ2RkbV9zdWJqZWN0X2RhdGEuUicpKQpybSh0ZXN0X2RhdGFfaGRkbV9mdWxsZml0LCB0ZXN0X2RhdGFfaGRkbV9yZWZpdCkKYGBgCgpgYGB7cn0KY2xlYW5fdGVzdF9kYXRhID0gcmVtb3ZlX2NvcnJlbGF0ZWRfdGFza192YXJpYWJsZXModGVzdF9kYXRhKQpgYGAKClJlbW92ZSBvdXRsaWVycyAoPjIuNSBTRCBhd2F5KQoKYGBge3J9CmNsZWFuX3Rlc3RfZGF0YSA9IGNiaW5kKHN1Yl9pZCA9IGNsZWFuX3Rlc3RfZGF0YSRzdWJfaWQsIGFzLmRhdGEuZnJhbWUoYXBwbHkoY2xlYW5fdGVzdF9kYXRhWywgLXdoaWNoKG5hbWVzKGNsZWFuX3Rlc3RfZGF0YSkgJWluJSBjKCJzdWJfaWQiKSldLCAyLCByZW1vdmVfb3V0bGllcnMpKSkKYGBgCgpUcmFuc2Zvcm0gc2tld2VkIHZhcmlhYmxlcwoKYGBge3J9Cm51bWVyaWNfY29scyA9IGdldF9udW1lcmljX2NvbHMoKQpudW1lcmljX2NvbHMgPSBudW1lcmljX2NvbHNbbnVtZXJpY19jb2xzICVpbiUgbmFtZXMoY2xlYW5fdGVzdF9kYXRhKSA9PSBUXQpjbGVhbl90ZXN0X2RhdGEgPSB0cmFuc2Zvcm1fcmVtb3ZlX3NrZXcoY2xlYW5fdGVzdF9kYXRhLCBudW1lcmljX2NvbHMpCmBgYAoKRHJvcCBzdWJqZWN0IGlkZW50aWZpZXIgY29sdW1uLCBtZWFuIGltcHV0ZSBhbmQgZHJvcCBjb2xzIHdpdGggbm8gdmFyaWFuY2UKCmBgYHtyfQpjbGVhbl90ZXN0X2RhdGFfc3RkID0gY2xlYW5fdGVzdF9kYXRhICU+JSBtdXRhdGVfaWYoaXMubnVtZXJpYywgc2NhbGUpCmNsZWFuX3Rlc3RfZGF0YV9zdGQgPSBjbGVhbl90ZXN0X2RhdGFfc3RkICU+JSBzZWxlY3QoLXN1Yl9pZCkKCiNtZWFuIGltcHV0YXRpb24KY2xlYW5fdGVzdF9kYXRhX3N0ZFtpcy5uYShjbGVhbl90ZXN0X2RhdGFfc3RkKV09MAoKI2Ryb3AgY29scyB3aXRoIG5vIHZhcmlhbmNlCmNsZWFuX3Rlc3RfZGF0YV9zdGQgPSBjbGVhbl90ZXN0X2RhdGFfc3RkICU+JQogIHNlbGVjdF9pZihmdW5jdGlvbihjb2wpIHNkKGNvbCkgIT0gMCkKYGBgCgpFeHRyYWN0IEVaIGFuZCBIRERNIHZhcmlhYmxlcwoKYGBge3J9CmNsZWFuX3Rlc3RfZGF0YV9leiA9IGNsZWFuX3Rlc3RfZGF0YV9zdGQgJT4lCiAgc2VsZWN0KGdyZXAoJ0VaJywgbmFtZXMoY2xlYW5fdGVzdF9kYXRhX3N0ZCksIHZhbHVlPVQpKQoKY2xlYW5fdGVzdF9kYXRhX2hkZG0gPSBjbGVhbl90ZXN0X2RhdGFfc3RkICU+JQogIHNlbGVjdChncmVwKCdoZGRtJywgbmFtZXMoY2xlYW5fdGVzdF9kYXRhX3N0ZCksIHZhbHVlPVQpKQpgYGAKClJlc2lkdWFsaXplIEFnZSBhbmQgU2V4IGVmZmVjdHMgCgpgYGB7cn0KZGF0YV9wYXRoID0gJy9Vc2Vycy96ZXluZXBlbmthdmkvRG9jdW1lbnRzL1BvbGRyYWNrTGFiTG9jYWwvU2VsZl9SZWd1bGF0aW9uX09udG9sb2d5L0RhdGEvJwpyZWxlYXNlID0gJ0NvbXBsZXRlXzAzLTI5LTIwMTgvJwpkYXRhc2V0ID0gJ2RlbW9ncmFwaGljX2hlYWx0aC5jc3YnCgpkZW1vZ3JhcGhpY3MgPSBnZXRfZGVtb2dyYXBoaWNzKGRhdGFzZXQgPSBwYXN0ZTAoZGF0YV9wYXRoLCByZWxlYXNlLCBkYXRhc2V0KSkKCmRlbW9ncmFwaGljcyA9IGRlbW9ncmFwaGljcyAlPiUKICBmaWx0ZXIoWCAlaW4lIGNsZWFuX3Rlc3RfZGF0YSRzdWJfaWQpCmBgYAoKYGBge3J9CmNsZWFuX3Rlc3RfZGF0YV9leiA9IGNiaW5kKGNsZWFuX3Rlc3RfZGF0YV9leiwgZGVtb2dyYXBoaWNzWyxjKCJBZ2UiLCAiU2V4IildKQoKcmVzX2NsZWFuX3Rlc3RfZGF0YV9leiA9IHJlc2lkdWFsaXplX2Jhc2VsaW5lKGNsZWFuX3Rlc3RfZGF0YV9leikKCnJlc19jbGVhbl90ZXN0X2RhdGFfZXoKYGBgCgojIyBQQ0Egb24gRVogdmFyaWFibGVzIG9mIHRlc3QgZGF0YQoKYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KcGNhX2V6X3QxX2NvbXBfbWV0cmljcyA9IGZpbmRfb3B0aW1hbF9jb21wb25lbnRzKHJlc19jbGVhbl90ZXN0X2RhdGFfZXosIG1pbmM9MiwgbWF4Yz0yMCwgbW9kZWwgPSAiUENBIikKYGBgCgpgYGB7cn0KcGNhX2V6X3QxX2NvbXBfbWV0cmljcwpgYGAKCmBgYHtyfQplel90MV9wY2FfMyA9IHByaW5jaXBhbChyZXNfY2xlYW5fdGVzdF9kYXRhX2V6LCBuZmFjdG9ycz0zLCByb3RhdGU9Im9ibGltaW4iKQpgYGAKCmBgYHtyfQpkYXRhLmZyYW1lKGV6X3QxX3BjYV8zJHZhbHVlcykgJT4lCiAgcmVuYW1lKGVpZyA9IGV6X3QxX3BjYV8zLnZhbHVlcykgJT4lCiAgYXJyYW5nZSgtZWlnKSAlPiUKICBtdXRhdGUodmFyX3BjdCA9IGVpZy9zdW0oZWlnKSoxMDAsCiAgICAgICAgIHBjID0gMTpuKCkpICU+JQogIGZpbHRlcihwYzwxMSklPiUKICBnZ3Bsb3QoYWVzKGZhY3RvcihwYyksIHZhcl9wY3QpKSsKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpKwogIHlsYWIoIlBlcmNlbnRhZ2Ugb2YgdmFyaWFuY2UgZXhwbGFpbmVkIikrCiAgeGxhYigiUHJpbmNpcGFsIGNvbXBvbmVudCIpCmBgYAoKYGBge3J9CmRhdGEuZnJhbWUoZXpfdDFfcGNhXzMkdmFsdWVzKSAlPiUKICByZW5hbWUoZWlnID0gZXpfdDFfcGNhXzMudmFsdWVzKSAlPiUKICBhcnJhbmdlKC1laWcpICU+JQogIG11dGF0ZSh2YXJfcGN0ID0gZWlnL3N1bShlaWcpKjEwMCwKICAgICAgICAgcGMgPSAxOm4oKSwKICAgICAgICAgdmFyX3BjdF9zaGlmdCA9IGxlYWQodmFyX3BjdCksCiAgICAgICAgIHZhcl9wY3RfZGlmZiA9IHZhcl9wY3QgLSB2YXJfcGN0X3NoaWZ0KSAlPiUKICBmaWx0ZXIocGM8MTEpICU+JQogIGFycmFuZ2UoLXZhcl9wY3RfZGlmZikKYGBgCgpgYGB7cn0KZXpfdDFfcGNhXzNfbG9hZGluZ3MgPSBhcy5kYXRhLmZyYW1lKGV6X3QxX3BjYV8zJGxvYWRpbmdzW10pCgplel90MV9wY2FfM19sb2FkaW5nc1thYnMoZXpfdDFfcGNhXzNfbG9hZGluZ3MpPDAuM109TkEKCnRtcCA9IGV6X3QxX3BjYV8zX2xvYWRpbmdzICU+JQogIG11dGF0ZShkdiA9IHJvdy5uYW1lcyguKSkgJT4lCiAgc2VsZWN0KGR2LCBUQzEsIFRDMiwgVEMzKSAlPiUKICBtdXRhdGUobnVtX2xvYWRpbmcgPSAzLShpcy5uYShUQzEpK2lzLm5hKFRDMikraXMubmEoVEMzKSApICkgJT4lCiAgZmlsdGVyKG51bV9sb2FkaW5nIT0wKSAlPiUKICBzZWxlY3QoLW51bV9sb2FkaW5nKSAlPiUKICBhcnJhbmdlKC1UQzEsIC1UQzIsIC1UQzMpICU+JQogIG11dGF0ZShvcmRlcl9udW0gPSAxOm4oKSwKICAgICAgICAgZHYgPSByZW9yZGVyKGR2LCAtb3JkZXJfbnVtKSkgJT4lCiAgc2VsZWN0KC1vcmRlcl9udW0pICU+JQogIGdhdGhlcihGYWN0b3IsIExvYWRpbmcsIC1kdikgJT4lCiAgbmEuZXhjbHVkZSgpICU+JQogIG11dGF0ZShsb2FkX3NpZ24gPSBmYWN0b3IoaWZlbHNlKExvYWRpbmc+MCwicG9zIiwibmVnIikpKQoKdmFyX3R5cGUgPSBpZmVsc2UoZ3JlcGwoImRyaWZ0IiwgdG1wJGR2KSwgIiM3ZmM5N2YiLAogICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoInRocmVzaCIsIHRtcCRkdiksICIjYmVhZWQ0IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgibm9uX2RlYyIsIHRtcCRkdiksICIjZmRjMDg2IiwgTkEpKSkgICAgICAgICAKdG1wJT4lICAgICAgICAgCiAgZ2dwbG90KGFlcyhkdiwgYWJzKExvYWRpbmcpLCBmaWxsPWxvYWRfc2lnbikpKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSsKICBmYWNldF93cmFwKH5GYWN0b3IsIG5yb3c9MSkrCiAgY29vcmRfZmxpcCgpKwogIHhsYWIoIiIpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChjb2xvciA9IHZhcl90eXBlKSkrCiAgeWxhYigiQWJzb2x1dGUgTG9hZGluZyIpCmBgYAoKIyMgRUZBIG9uIEVaIHZhcmlhYmxlcyBvZiB0ZXN0IGRhdGEKCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmVmYV9lel90MV9jb21wX21ldHJpY3MgPSBmaW5kX29wdGltYWxfY29tcG9uZW50cyhyZXNfY2xlYW5fdGVzdF9kYXRhX2V6LCBmbSA9ICJtaW5yZXMiLCBtaW5jPTIpCmBgYAoKYGBge3J9CmVmYV9lel90MV9jb21wX21ldHJpY3MKYGBgCgpgYGB7cn0KZXpfdDFfZmFfOCA9IGZhKHJlc19jbGVhbl90ZXN0X2RhdGFfZXosIGVmYV9lel90MV9jb21wX21ldHJpY3MkY29tcFsxXSwgcm90YXRlPSdvYmxpbWluJywgZm09J21pbnJlcycsIHNjb3Jlcz0ndGVuQmVyZ2UnKQpgYGAKCmBgYHtyfQplel90MV9mYV8zID0gZmEocmVzX2NsZWFuX3Rlc3RfZGF0YV9leiwgMywgcm90YXRlPSdvYmxpbWluJywgZm09J21pbnJlcycsIHNjb3Jlcz0ndGVuQmVyZ2UnKQpgYGAKCmBgYHtyfQphbm92YShlel90MV9mYV8zLCBlel90MV9mYV84KQpgYGAKCmBgYHtyfQplel90MV9mYV8zX2xvYWRpbmdzID0gYXMuZGF0YS5mcmFtZShlel90MV9mYV8zJGxvYWRpbmdzW10pCgplel90MV9mYV8zX2xvYWRpbmdzW2Ficyhlel90MV9mYV8zX2xvYWRpbmdzKTwwLjNdPU5BCgp0bXAgPSBlel90MV9mYV8zX2xvYWRpbmdzICU+JQogIG11dGF0ZShkdiA9IHJvdy5uYW1lcyguKSkgJT4lCiAgc2VsZWN0KGR2LCBNUjEsIE1SMiwgTVIzKSAlPiUKICBtdXRhdGUobnVtX2xvYWRpbmcgPSAzLShpcy5uYShNUjEpK2lzLm5hKE1SMikraXMubmEoTVIzKSApICkgJT4lCiAgZmlsdGVyKG51bV9sb2FkaW5nIT0wKSAlPiUKICBzZWxlY3QoLW51bV9sb2FkaW5nKSAlPiUKICBhcnJhbmdlKC1NUjEsIC1NUjIsIC1NUjMpICU+JQogIG11dGF0ZShvcmRlcl9udW0gPSAxOm4oKSwKICAgICAgICAgZHYgPSByZW9yZGVyKGR2LCAtb3JkZXJfbnVtKSkgJT4lCiAgc2VsZWN0KC1vcmRlcl9udW0pICU+JQogIGdhdGhlcihGYWN0b3IsIExvYWRpbmcsIC1kdikgJT4lCiAgbmEuZXhjbHVkZSgpICU+JQogIG11dGF0ZShsb2FkX3NpZ24gPSBmYWN0b3IoaWZlbHNlKExvYWRpbmc+MCwicG9zIiwibmVnIikpKQoKdmFyX3R5cGUgPSBpZmVsc2UoZ3JlcGwoImRyaWZ0IiwgdG1wJGR2KSwgIiM3ZmM5N2YiLAogICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoInRocmVzaCIsIHRtcCRkdiksICIjYmVhZWQ0IiwKICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgibm9uX2RlYyIsIHRtcCRkdiksICIjZmRjMDg2IiwgTkEpKSkgICAgICAgICAKdG1wJT4lICAgICAgICAgCiAgZ2dwbG90KGFlcyhkdiwgYWJzKExvYWRpbmcpLCBmaWxsPWxvYWRfc2lnbikpKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSsKICBmYWNldF93cmFwKH5GYWN0b3IsIG5yb3c9MSkrCiAgY29vcmRfZmxpcCgpKwogIHhsYWIoIiIpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChjb2xvciA9IHZhcl90eXBlKSkrCiAgeWxhYigiQWJzb2x1dGUgTG9hZGluZyIpCmBgYAoKYGBge3J9CmV6X3QxX2ZhXzhfbG9hZGluZ3MgPSBhcy5kYXRhLmZyYW1lKGV6X3QxX2ZhXzgkbG9hZGluZ3NbXSkKCmV6X3QxX2ZhXzhfbG9hZGluZ3NbYWJzKGV6X3QxX2ZhXzhfbG9hZGluZ3MpPDAuM109TkEKCnRtcCA9IGV6X3QxX2ZhXzhfbG9hZGluZ3MgJT4lCiAgbXV0YXRlKGR2ID0gcm93Lm5hbWVzKC4pKSAlPiUKICBzZWxlY3QoZHYsIE1SMSwgTVIyLCBNUjMsIE1SNCwgTVI1LCBNUjYsIE1SNywgTVI4KSAlPiUKICBtdXRhdGUobnVtX2xvYWRpbmcgPSA4LShpcy5uYShNUjEpK2lzLm5hKE1SMikraXMubmEoTVIzKStpcy5uYShNUjQpK2lzLm5hKE1SNSkraXMubmEoTVI2KStpcy5uYShNUjcpK2lzLm5hKE1SOCkgKSApICU+JQogIGZpbHRlcihudW1fbG9hZGluZyE9MCkgJT4lCiAgc2VsZWN0KC1udW1fbG9hZGluZykgJT4lCiAgYXJyYW5nZSgtTVIxLCAtTVIyLCAtTVIzLC1NUjEsIC1NUjUsIC1NUjYsLU1SNywgLU1SOCkgJT4lCiAgbXV0YXRlKG9yZGVyX251bSA9IDE6bigpLAogICAgICAgICBkdiA9IHJlb3JkZXIoZHYsIC1vcmRlcl9udW0pKSAlPiUKICBzZWxlY3QoLW9yZGVyX251bSkgJT4lCiAgZ2F0aGVyKEZhY3RvciwgTG9hZGluZywgLWR2KSAlPiUKICBuYS5leGNsdWRlKCkgJT4lCiAgbXV0YXRlKGxvYWRfc2lnbiA9IGZhY3RvcihpZmVsc2UoTG9hZGluZz4wLCJwb3MiLCJuZWciKSkpCgp2YXJfdHlwZSA9IGlmZWxzZShncmVwbCgiZHJpZnQiLCB0bXAkZHYpLCAiIzdmYzk3ZiIsCiAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgidGhyZXNoIiwgdG1wJGR2KSwgIiNiZWFlZDQiLAogICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJub25fZGVjIiwgdG1wJGR2KSwgIiNmZGMwODYiLCBOQSkpKSAgICAgICAgIAp0bXAlPiUgICAgICAgICAKICBnZ3Bsb3QoYWVzKGR2LCBhYnMoTG9hZGluZyksIGZpbGw9bG9hZF9zaWduKSkrCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpKwogIGZhY2V0X3dyYXAofkZhY3RvciwgbnJvdz0xKSsKICBjb29yZF9mbGlwKCkrCiAgeGxhYigiIikrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gdmFyX3R5cGUpKSsKICB5bGFiKCJBYnNvbHV0ZSBMb2FkaW5nIikKYGBgCgoKIyMgUENBIG9uIEhERE0gdmFyaWFibGVzIG9mIHRlc3QgZGF0YQoKIyMgRUZBIG9uIEhERE0gdmFyaWFibGVzIG9mIHRlc3QgZGF0YQoKIyMgUENBIG9uIEVaIHZhcmlhYmxlcyBvZiByZXRlc3QgZGF0YQoKIyMgRUZBIG9uIEVaIHZhcmlhYmxlcyBvZiByZXRlc3QgZGF0YQoKIyMgUENBIG9uIEhERE0gdmFyaWFibGVzIG9mIHJldGVzdCBkYXRhCgojIyBFRkEgb24gSERETSB2YXJpYWJsZXMgb2YgcmV0ZXN0IGRhdGE=